#include"Snake.h"


GameAtt attribute; //记录游戏属性,游戏主题,地图大小等
vector allgameresult; //记录游戏记录

//全局的一些共用变量
const char* massage1[9] = {" 开始游戏 ",  " 历史记录 ", "   设置   ", "   退出   ",
                               "贪吃蛇大作战", "------------------------------", 
                               "|                            |", "->", "  "};
const char* massage2[3] = {"小","中","大"};
const char* massage3[3] = {"慢","中","快"};
const char* massage4[3] = {"赢了  ", "吃自己", "撞墙了"};
const wchar_t* phychar[5] = { L"◉", L"☢",L"◍", L"▣", L"▢"};

//初始化
void Init()
{ 
    //初始化属性设置
    InitAttribute(&attribute);
    //加载游戏记录
    InitResult(&allgameresult);

    //ncurses库中的一些设置,配置颜色,字符等调用
    setlocale(LC_ALL, "");

    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    curs_set(0);

    start_color();
    init_pair(1, COLOR_WHITE, COLOR_BLACK);   
    init_pair(2, COLOR_BLUE, COLOR_BLACK);   
    init_pair(3, COLOR_YELLOW, COLOR_BLACK); 
    init_pair(4, COLOR_RED, COLOR_BLACK); 
    init_pair(5, COLOR_GREEN, COLOR_BLACK);
    init_pair(6, COLOR_WHITE, COLOR_GREEN);    
    init_pair(7, COLOR_WHITE, COLOR_YELLOW);

    

}

//打印游戏菜单
int ShowMenu(int option)
{
    //游戏菜单相关字符串打印
    int input = 0;
    attron(COLOR_PAIR(2) | A_BOLD);
    mvprintw(LINES/2 - 8, COLS / 2 - 6, massage1[4]);
    attroff(COLOR_PAIR(2) | A_BOLD);

    for(int i = 0; i < 14; i++)
    {

        if(i == 0 || i == 13)
        {
            mvprintw((LINES - MENU_LINES) / 2+ i, (COLS - MENU_COLS) / 2, massage1[5]);

        }
        else
        {
            mvprintw((LINES - MENU_LINES) / 2+ i, (COLS - MENU_COLS) / 2, massage1[6]);

        }
    }

    attron(COLOR_PAIR(6) | A_BOLD);
    for(int i = 0; i < 4; i++)
    {
        mvprintw(LINES/2 -3 + (i * 2), COLS/2 - 5 , massage1[i]);
    }
    attroff(COLOR_PAIR(6) | A_BOLD);

    mvprintw(LINES/2 -3 + (option * 2), COLS/2 - 8 , massage1[7]);

    //读取用户选择
    while(input != ENTRE)
   {
    input = getch();
    //将选择规格化,功能相同的按键统一为一个变量
    FROMINPUT(input);
    mvprintw(LINES/2 -3 + (option * 2), COLS/2 - 8 , massage1[8]);
    if(input == KEY_UP) 
    {
        option = (option == 0 ? 3 : option - 1);
    }
    else if(input == KEY_DOWN) 
    {
        option = (option == 3 ? 0 : option + 1);
    }
    mvprintw(LINES/2 -3 + (option * 2), COLS/2 - 8 , massage1[7]);
    refresh();
    }
    return option;
}
//执行选项
void RunOption(int option)
{
    switch (option)
    {
        case 0:
        Game(); break;
        case 1:
        HistoryScores(); break;
        case 2:
        Setting(); break;
        case 3:
        End(); break;
        default:
        perror("RunOption");
        exit(-1);
    }

}

//游戏时,需要一个游戏画面窗口和按键提示窗口,
//ShowGameMap生成游戏画面窗口并返回该窗口指针
WINDOW* ShowGameMap()
{
     WINDOW *scrgame = newwin(attribute.maplength, attribute.mapwidth, (LINES - attribute.maplength) / 2,
     (COLS - attribute.mapwidth) / 2); 
    box(scrgame, 0, 0); 
    wrefresh(scrgame);
    return scrgame;
}
//ShowResult生成按键提示窗口,返回窗口指针,该窗口内除了按键提示,还有游戏成绩的打印
WINDOW* ShowResult()
{
    clear();
    refresh();
    int length = attribute.maplength;
    int width = 18;
    WINDOW *scrscore = newwin(length, width, (LINES - length) / 2,
     (COLS - attribute.mapwidth) / 2 + attribute.mapwidth - 1); 
    box(scrscore, 0, 0); 
    mvwprintw(scrscore, length / 2 - 5, 3, "得分：");
    mvwprintw(scrscore, length / 2 - 3, 1, "----------------");
    mvwprintw(scrscore, length / 2 - 2, 4, "说明：");
    mvwprintw(scrscore, length / 2 - 1, 1, "  上下左右或者  ");
    mvwprintw(scrscore, length / 2 - 0, 1, "    wasd按键   ");
    mvwprintw(scrscore, length / 2 + 1, 1, "  控制蛇的移动  ");
    mvwprintw(scrscore, length / 2 + 2, 1, "  吃豆子得分加1 ");
    mvwprintw(scrscore, length / 2 + 3, 1, "  碰到蛇身失败  ");
    mvwprintw(scrscore, length / 2 + 4, 1, "  碰到墙面失败  ");
    mvwprintw(scrscore, length / 2 + 5, 1, "  按下空格暂停  ");
    wrefresh(scrscore);
    return scrscore;
     
}
//运行游戏
void Game()
{
    //生成两个窗口并拿到窗口指针
    WINDOW* scrscore = ShowResult(); 
    WINDOW* scrgame = ShowGameMap(); 

    Snake* sna = GetSnake();  //或者贪吃蛇数据结构,包含方向,成绩,蛇头蛇身位置等信息
    SnakeMove(sna, scrgame, scrscore); //在窗口内运行游戏画面,包括蛇的移动,豆子,成绩等信息
    DestroySnake(sna); //游戏结束后回收数据结构

    //清除窗口回收资源
    delwin(scrgame);
    delwin(scrscore);
    clear();
    refresh();
}

//打印历史记录
void HistoryScores()
{
    //打印相关提示符和窗口
    const char* listh = "         游戏时间          成绩     地图大小   游戏速度     结果";
    const char* tips1  = "        按键提示： ↑ 上一页  ↓ 下一页  q/Q 退出   back 清空记录 ";
    const char* tips2  = "清除后将无法回复，是否确认清除";
    const char* tips3  = "y: 确认清除  n: 取消清除";
    const char* tips4  = "清除成功";
    const char* linenull = "                                                                 ";
    
    clear();
    refresh();
    WINDOW* scrresult = newwin(RESULT_LINES, RESULT_COLS, 
    (LINES - RESULT_LINES) / 2, (COLS - RESULT_COLS) / 2);
    WINDOW* scrtips = newwin(1, RESULT_COLS, 
    (LINES - RESULT_LINES) / 2 + RESULT_LINES + 1, (COLS - RESULT_COLS) / 2);
    box(scrresult, 0, 0);
    mvwprintw(scrtips, 0, 0, tips1);
    wrefresh(scrtips);

    mvwprintw(scrresult, 1, 1, listh);
    wrefresh(scrresult);
    keypad(scrresult, TRUE);
    int input = KEY_DOWN;
    int max = RESULT_LINES - 3;
    int pos = 0;

    //可翻页的历史记录，按下q退出
    do
    {
        //设置清空历史记录的按键
        if(input == KEY_BACKSPACE || input == 8) 
        {
            for(int i = 0;i<=max;i++)
            {
                mvwprintw(scrresult, 1 + i, 3, linenull);
            }
                mvwprintw(scrresult, RESULT_LINES / 2 -2 , RESULT_COLS / 2 - 15,tips2);
                mvwprintw(scrresult, RESULT_LINES / 2  , RESULT_COLS  / 2 - 13,tips3);  
            do
            {
                input = wgetch(scrresult);
                if(input == 'y' || input == 'Y')
                {
                    DestroyVec(&allgameresult);
                    InitVector(&allgameresult);
                    mvwprintw(scrresult, RESULT_LINES / 2 -2 , 3,linenull);
                    mvwprintw(scrresult, RESULT_LINES / 2  , 3, linenull);  
                    mvwprintw(scrresult, RESULT_LINES / 2  ,RESULT_COLS  / 2 - 4,tips4);  
                    wrefresh(scrresult);
                    sleep(2);
                    break;
                }
            }while(input != 'n' && input != 'N');
            mvwprintw(scrresult, 1, 1, listh);
        }
        type1* tmp = NULL;
        tmp = allgameresult.head + pos * max;
        //打印历史记录
        for(int i = 0; i < max; i++)
        {
            if(i + pos * max < allgameresult.size)
            {
                mvwprintw(scrresult, 2 + i, 3, tmp[i]);  
            }
            else mvwprintw(scrresult, 2 + i, 3, linenull);
        }
        input = wgetch(scrresult);
        if(input == KEY_UP && pos != 0) pos--;
        else if(input == KEY_DOWN && (pos+1) * max < allgameresult.size) pos++;
        wrefresh(scrresult);
    } while (input != 'q' && input != 'Q');
    
    keypad(scrresult, FALSE);
    wclear(scrtips);
    wrefresh(scrtips);
    delwin(scrtips);
    wclear(scrresult);
    wrefresh(scrresult);
    delwin(scrresult);

}

//打印设置菜单
int ShowSetMenu()
{
    WINDOW* set = newwin(SETTING_LINES, SETTING_COLS, 
    (LINES - SETTING_LINES) / 2, (COLS - SETTING_COLS) / 2);
    box(set, 0, 0);
    keypad(set, TRUE);

    int option = 0;
    int input = 0;

    mvwprintw(set, SETTING_LINES/2 - 2, SETTING_COLS/2 - 4, "主题设置");
    mvwprintw(set, SETTING_LINES/2 , SETTING_COLS/2 - 4, "难度设置");
    mvwprintw(set, SETTING_LINES/2 + 2, SETTING_COLS/2 - 4, "退出设置");


    mvwprintw(set, SETTING_LINES/2 + option * 2 - 2,  SETTING_COLS/2 - 7 , "->");
    
    while(input != ENTRE)
    {
        input = wgetch(set);
        FROMINPUT(input);
        mvwprintw(set, SETTING_LINES/2 + option * 2 - 2,  SETTING_COLS/2 - 7 , "  ");
        if(input == KEY_UP) 
        {
            option = (option == 0 ? 2 : option - 1);
        }
        else if(input == KEY_DOWN) 
        {
            option = (option == 2 ? 0 : option + 1);
        }
        mvwprintw(set, SETTING_LINES/2 + option * 2 - 2,  SETTING_COLS/2 - 7 , "->");
        wrefresh(set);
    }

    wclear(set);
    wrefresh(set);
    delwin(set);

    return option;
}

//获取用户主题设置选项
int GetThemSetOption(int cnt, WINDOW* scrthem, GameAtt* tmp, int ch)
{
    int input = 0;
    int option = 0;
    int min = 0;
    int max = 4;
    if(cnt == 1)
    {
        cnt = 1;
        mvwaddwstr(scrthem, SETTING_LINES/2 - 1 , SETTING_COLS/2 - 2, phychar[attribute.color.snahead]);
        while(cnt != 5)
        {
            wattron(scrthem, COLOR_PAIR(cnt+1) );
            mvwaddwstr(scrthem, SETTING_LINES/2 - 1 + cnt, SETTING_COLS/2 - 2, phychar[attribute.color.snahead]);
            wattroff(scrthem, COLOR_PAIR(cnt+1) );
            cnt++;
        }
        cnt = 1;
    }
    else if(cnt == 2)
    {
        max  = 1;
    }
    else if(cnt == 3)
    {
        cnt = 2;
        min = option = 2;
    }
    mvwprintw(scrthem, SETTING_LINES/2 - 1 + option,  SETTING_COLS/2 - 22 + 17 * cnt ,  "->");
    
    wrefresh(scrthem);
    wattron(scrthem, COLOR_PAIR(tmp->color.headcol) );
    mvwaddwstr(scrthem,  SETTING_LINES/2 - 4, SETTING_COLS/2 + 4, phychar[tmp->color.snahead]);
    wattroff(scrthem, COLOR_PAIR(tmp->color.headcol) );
    if(tmp->color.headcol == 1)
    mvwaddwstr(scrthem,  SETTING_LINES/2 - 4, SETTING_COLS/2 + 4, phychar[tmp->color.snahead]);
    wattron(scrthem, COLOR_PAIR(tmp->color.phycol) );
    int i = -6;
    while(i != 4)
    {
        mvwaddwstr(scrthem, SETTING_LINES/2 - 4, SETTING_COLS/2 + i, phychar[tmp->color.snaphy]);
        i += 2;
    }
    wattroff(scrthem, COLOR_PAIR(tmp->color.phycol) );
    while(input != ENTRE)
    {
        input = wgetch(scrthem);
        FROMINPUT(input);
        mvwprintw(scrthem, SETTING_LINES/2 - 1 + option,  SETTING_COLS/2 - 22 + 17 * cnt,  "  ");
        if(input == KEY_UP) 
        {
            option = (option == min ? max : option - 1);
        }
        else if(input == KEY_DOWN) 
        {
            option = (option == max ? min : option + 1);
        }
        mvwprintw(scrthem, SETTING_LINES/2 - 1 + option,  SETTING_COLS/2 - 22 + 17 * cnt ,  "->");
        wrefresh(scrthem);
    }
    return option;
}

//主题设置
int ThemeSet(GameAtt* tmp)
{
    //相关窗口和提示信息的绘制
    WINDOW* scrthem = newwin(SETTING_LINES, SETTING_COLS, 
    (LINES - SETTING_LINES) / 2, (COLS - SETTING_COLS) / 2);
    box(scrthem, 0, 0);
    //wgetch(scrthem);
    keypad(scrthem, TRUE);

    mvwprintw(scrthem, SETTING_LINES/2 - 5, SETTING_COLS/2 - 4, "当前主题");
    mvwprintw(scrthem, SETTING_LINES/2 - 2 ,  SETTING_COLS/2 - 20 , "形状");
    mvwprintw(scrthem, SETTING_LINES/2 - 2 ,  SETTING_COLS/2 - 3 , "颜色");
    mvwprintw(scrthem, SETTING_LINES/2 - 2 ,  SETTING_COLS/2 + 15 , "应用部位");

    mvwprintw(scrthem, SETTING_LINES/2 - 1 , SETTING_COLS/2 + 17, "蛇头");
    mvwprintw(scrthem, SETTING_LINES/2     , SETTING_COLS/2 + 17, "蛇身");
    mvwprintw(scrthem, SETTING_LINES/2 + 1 , SETTING_COLS/2 + 15, "继续选择");
    mvwprintw(scrthem, SETTING_LINES/2 + 2 , SETTING_COLS/2 + 15, "取消更改");
    mvwprintw(scrthem, SETTING_LINES/2 + 3  , SETTING_COLS/2 + 15, "保存退出");
    int cnt = 0;
    //展示所有字符,供用户选择
    while(cnt!=5)
    {
        mvwaddwstr(scrthem, SETTING_LINES/2 - 1 + cnt, SETTING_COLS/2 - 19, phychar[cnt]);
        cnt++;
    }
    cnt = 0;
    //展示所有颜色,供用户选择
     while(cnt != 5)
    {
        wattron(scrthem, COLOR_PAIR(cnt+1) );
        mvwaddwstr(scrthem, SETTING_LINES/2 - 1 + cnt, SETTING_COLS/2 - 2, phychar[attribute.color.snahead]);
        wattroff(scrthem, COLOR_PAIR(cnt+1) );
        cnt++;
    }
    wrefresh(scrthem);

    int ch = GetThemSetOption(0, scrthem, tmp, tmp->color.snahead); //获取用户所选择的符号
    int col = GetThemSetOption(1, scrthem, tmp, ch) + 1;  //获取用户所选择的颜色
    int opt = GetThemSetOption(2, scrthem, tmp, ch);   //获取用户所要应用的对象(蛇头或者蛇身)

    //将符号和颜色保存到蛇头
    if(opt == 0)
    {
        tmp->color.headcol = col;
        tmp->color.snahead = ch;
    }
    //将符号和颜色保存到蛇身
    else if(opt == 1)
    {
        tmp->color.phycol = col;
        tmp->color.snaphy = ch;
    }

    //获取用户最后的选项(继续选择, 取消更改,保存退出)
    opt = GetThemSetOption(3, scrthem, tmp, ch);

    //取消更改,重新设置tmp
    if(opt == 3)
    {
        tmp->color.headcol = attribute.color.headcol;
        tmp->color.phycol = attribute.color.phycol;
        tmp->color.snahead = attribute.color.snahead;
        tmp->color.snaphy = attribute.color.snaphy;
    }
    //保存退出,保存到全局attribute
    else if(opt == 4)
    {
        attribute.color.headcol = tmp->color.headcol;
        attribute.color.phycol = tmp->color.phycol;
        attribute.color.snahead = tmp->color.snahead;
        attribute.color.snaphy = tmp->color.snaphy;
    }

    //清除资源回收
    wclear(scrthem);
    wrefresh(scrthem);
    delwin(scrthem);
    return opt;
}


//获取用户难度设置选项
int GetGradSetOption(int cnt,  WINDOW* scrgrad, GameAtt* tmp, const char* mess1[], const char* mess2[])
{
    int input = 0;
    int option = 0;
    int min = 0;
    int max = 2;
   
    mvwprintw(scrgrad, SETTING_LINES/2 - 2 + option * 2,  SETTING_COLS/2 - 21 + 17 * cnt ,  "->");
    mvwprintw(scrgrad, SETTING_LINES/2 + 4 , SETTING_COLS/2 - 18, mess1[GET_MAPPOS(tmp->maplength)]);
    mvwprintw(scrgrad, SETTING_LINES/2 + 4 , SETTING_COLS/2 - 1,  mess2[GET_SPEEDPOS(tmp->speed)]);
    while(input != ENTRE)
    {
        input = wgetch(scrgrad);
        FROMINPUT(input);
        mvwprintw(scrgrad, SETTING_LINES/2 - 2 + option * 2,  SETTING_COLS/2 - 21 + 17 * cnt ,  "  ");
        if(input == KEY_UP) 
        {
            option = (option == min ? max : option - 1);
        }
        else if(input == KEY_DOWN) 
        {
            option = (option == max ? min : option + 1);
        }
        mvwprintw(scrgrad, SETTING_LINES/2 - 2 + option * 2,  SETTING_COLS/2 - 21 + 17 * cnt ,  "->");
        wrefresh(scrgrad);
    }
    return option;
}

//游戏难度设置
int GameGradeSet(GameAtt* tmp)
{
   //绘制相关窗口和提示信息
     WINDOW* scrgrad = newwin(SETTING_LINES, SETTING_COLS, 
    (LINES - SETTING_LINES) / 2, (COLS - SETTING_COLS) / 2);
    box(scrgrad, 0, 0);
    keypad(scrgrad, TRUE);

    int option = 0;
    int input = 0;
    mvwprintw(scrgrad, SETTING_LINES/2 - 4 ,  SETTING_COLS/2 - 20 , "地图大小");
    mvwprintw(scrgrad, SETTING_LINES/2 - 4 ,  SETTING_COLS/2 - 3 , "游戏速度");
    mvwprintw(scrgrad, SETTING_LINES/2 - 4 ,  SETTING_COLS/2 + 16 , "设置选项");
    mvwprintw(scrgrad, SETTING_LINES/2 + 4  , SETTING_COLS/2 - 30, "当前设置");


    for(int i = 0; i < 3; i++)
    {
    mvwprintw(scrgrad, SETTING_LINES/2 - 2 + i * 2, SETTING_COLS/2 - 18, massage2[i]);
    mvwprintw(scrgrad, SETTING_LINES/2 - 2 + i * 2, SETTING_COLS/2 - 1,  massage3[i]);

    }

    mvwprintw(scrgrad, SETTING_LINES/2 - 2 , SETTING_COLS/2 + 16, "重新选择");
    mvwprintw(scrgrad, SETTING_LINES/2     , SETTING_COLS/2 + 16, "取消更改");
    mvwprintw(scrgrad, SETTING_LINES/2 + 2  , SETTING_COLS/2 + 16, "保存退出");
    wrefresh(scrgrad);

    //获取用户对地图大小的选择
    int opt = GetGradSetOption(0, scrgrad, tmp, massage2, massage3);
    //将选择保存在临时设置变量tmp里
    tmp->maplength = GET_MAPLEN(opt);
    tmp->mapwidth = GET_MAPWID(opt);
    
    //获取用户对蛇移动速度的选择
    opt = GetGradSetOption(1, scrgrad, tmp, massage2, massage3);
    //保存在临时变量tmp里
    tmp->speed = GET_SPEED(opt);
    
    //获取用户最后所确认的选项,
    opt = GetGradSetOption(2, scrgrad, tmp, massage2, massage3);

    //选项1为取消更改，将tmp重置
    if(opt == 1)
    {
        tmp->maplength = attribute.maplength;
        tmp->mapwidth = attribute.mapwidth;
        tmp->speed = attribute.speed;

    }
    //选项2为保存退出,将tmp内容拷贝到全局设置attribute里,
    else if(opt == 2)
    {
        attribute.maplength = tmp->maplength;
        attribute.mapwidth = tmp->mapwidth;
        attribute.speed = tmp->speed;
    }

    //清除窗口回收资源
    wclear(scrgrad);
    wrefresh(scrgrad);
    delwin(scrgrad);
    return opt;
}

//游戏设置
void Setting()
{
    clear();
    refresh();
    int option = 0;
    GameAtt tmp = attribute; //将用户本次设置存于临时变量tmp里
    do
    {
        option = ShowSetMenu(); //打印设置菜单
        if(option == 0)                      
        {
            while(2 == ThemeSet(&tmp)){}; //进行主题设置,直到用户退出
        }
        else if(option == 1)
        {
            while(0 == GameGradeSet(&tmp)){}; //进行难度设置,直到用户退出
        }
    }while(option != 2);
    

}

//结束时,保存历史记录和设置等数据,并且释放全局相关资源
void End()
{

    //保存设置数据
    FILE* conf = fopen("config.txt","w");
    fprintf(conf,"蛇头颜色：%d, 蛇头符号序号：%d\n", attribute.color.headcol,attribute.color.snahead);
    fprintf(conf,"蛇身颜色：%d, 蛇身符号序号：%d\n", attribute.color.phycol,attribute.color.snaphy);
    fprintf(conf,"地图长度：%d, 地图宽度：%d\n", attribute.maplength,attribute.mapwidth);
    fprintf(conf,"蛇的移动间隔: %d\n", attribute.speed);
    fclose(conf);

    //保存游戏记录数据
    const char* listh = "         游戏时间          成绩     地图大小   游戏速度     结果\n";
    FILE* res = fopen("gamehestroy.txt","w");
    fprintf(res, listh);
    type1* tmp = NULL;
    tmp = allgameresult.head;
    for(int i = 0; i < allgameresult.size; i++)
    {
        fprintf(res,"%s\n",tmp[i]);  
    }
    fclose(res);
    
    DestroyVec(&allgameresult); //释放开辟的堆上的资源

    endwin();
}

//初始化历史成绩数据结构
void InitResult(vector* allgameresult)
{
    //从文件里加载游戏记录,记录在顺序表里面
    InitVector(allgameresult);
    FILE* res = fopen("gamehestroy.txt","r");
    char* buffer = (char*)calloc(BUFFERSIZE, 1);
    if(buffer == NULL)
    {
        perror("InitResult->malloc");
        exit(-1);
    }
    if(res != NULL)
    {
        //读出表头
        fgets(buffer, BUFFERSIZE, res);
        fgets(buffer, BUFFERSIZE, res);

        int ret = 0;
        //一行为一条记录,按行读出,存于堆空间上
        while(fgets(buffer, BUFFERSIZE, res) != NULL)
        {
            buffer[strlen(buffer) - 1] = '\0';
            Push(allgameresult, buffer);
            buffer = (char*)calloc(BUFFERSIZE, 1);
        }
        fclose(res);
    }
    

}

//初始化全局设置数据结构
void InitAttribute(GameAtt* att)
{
    //从配置文件里加载进来,配置文件在游戏结束会保存最近的设置
    FILE* conf = fopen("config.txt","r");
    if(conf == NULL)
    {
        att->color.headcol = 1;
        att->color.phycol = 1;
        att->color.snahead = 0;
        att->color.snaphy = 1;

        att->maplength = MAP_LINES1;
        att->mapwidth = MAP_COLS1;
        att->speed = GAME_SPEED1;
    }
    else
    {
    fscanf(conf,"蛇头颜色：%d, 蛇头符号序号：%d\n", &att->color.headcol,&att->color.snahead);
    fscanf(conf,"蛇身颜色：%d, 蛇身符号序号：%d\n", &att->color.phycol,&att->color.snaphy);
    fscanf(conf,"地图长度：%d, 地图宽度：%d\n",  &att->maplength,&att->mapwidth);
    fscanf(conf,"蛇的移动间隔: %d\n", &att->speed);
    fclose(conf);

    }
}

//初始化贪吃蛇数据结构
Snake* GetSnake()
{

    //初始化贪吃蛇数据结构
    Snake* ret = (Snake*)malloc(sizeof(Snake));
    ret->dire = SRIGHT;
    ret->score = 0;
    ret->physics = GetList();
    type tmp = {1,1};
    PushFront(ret->physics, tmp);  //默认出生位置为1,1,方向为向右
    return ret;
}

//绘制设置窗口和相关提示信息,并读取用户选择
int ShowPause()
{
    int length = attribute.maplength;
    int width = attribute.mapwidth;
    int input = 0;
    int option = 0;

    WINDOW *pause = newwin(length, width, (LINES - length) / 2,
    (COLS - width) / 2); 

    box(pause, 0, 0); //绘制窗口 
    keypad(pause, TRUE); //启用功能键

    //打印相关提示信息
    mvwprintw(pause, length/2 - 1, width/2 - 4, "继续游戏");
    mvwprintw(pause, length/2 + 1, width/2 - 4, "退出游戏");
    mvwprintw(pause, length/2 + option * 2 - 1,  width/2 - 7 , "->");

    //读取用户选择
    while(input != ENTRE)
    {
        input = wgetch(pause);
        FROMINPUT(input);
        mvwprintw(pause, length/2 + option * 2 - 1,  width/2 - 7 , "  ");
        if(input == KEY_UP) 
        {
            option = (option == 0 ? 1 : option - 1);
        }
        else if(input == KEY_DOWN ) 
        {
            option = (option == 1 ? 0 : option + 1);
        }
        mvwprintw(pause, length/2 + option * 2 - 1,  width/2 - 7 , "->");
        wrefresh(pause);
    }
    mvwprintw(pause, length/2 - 1, width/2 - 7, "          ");
    mvwprintw(pause, length/2 + 1, width/2 - 7, "          ");
    wrefresh(pause);

    //清除数据结构回收资源
    delwin(pause);
    return option;
}
void SnakeMove(Snake* sna, WINDOW* scrgame, WINDOW* scrscore)
{
    //设置随机数种子,随机数用来生成豆子位置
    srand((unsigned)time(NULL));
    nodelay(scrgame, TRUE);
    keypad(scrgame, TRUE);

    Node* head = sna->physics->head->next; //蛇头指针
    type val = head->val; //
    
    int falg = TRUE;
    int input = 0;
    char bitmap[MAP_LINES3][MAP_COLS3/2 + 1] = {0}; //位图,记录蛇的身体分布,用来判断是否吃到自己身体
    bitmap[head->val.y][head->val.x / 2 + 1] = 1;  //初始化,蛇头开始默认在1,1位置
    
 
    wattron(scrgame, COLOR_PAIR(attribute.color.headcol) );
    mvwaddwstr(scrgame, head->val.y, head->val.x, phychar[attribute.color.snahead]);
    wattroff(scrgame, COLOR_PAIR(attribute.color.headcol) );
    wrefresh(scrgame);



    type nap = {head->val.y,head->val.x};  //nap用来记录豆子位置

    //随机在蛇身和蛇头所占位置外生成豆子,
    while(bitmap[nap.y][nap.x / 2 + 1] == 1)
    {
        nap.x = rand() % (attribute.mapwidth - 3) ;
        if(nap.x % 2 == 0) nap.x++;
        nap.y = rand() % (attribute.maplength - 2) + 1;
    }
    //自动吃豆满分测试
    // nap.x = 3;
    // nap.y = 1;

    //计算最大分数,用来判断胜利,
    int maxscore = (attribute.maplength - 2) * (attribute.mapwidth / 2 - 1) - 1;
    while(falg)
    {
        input = wgetch(scrgame); //读取用户输入,为蛇的移动方向
        FROMINPUT(input);  //规格化用户输入
        //flushinp();

        mvwaddwstr(scrgame, nap.y, nap.x, NAP); //打印豆子
        wrefresh(scrgame);   //立刻刷新
        input = input == ENTRE ? ERR : input; //ENTER为无关输入,在其他地方有用,但是只有一个宏函数,会在这里规格化处理时处理不到,因此特判处理 

        //如果用户输入为空格,则暂停,执行暂停相关程序
        if(input == ' ')
        {
            int ret = ShowPause();  //绘制窗口和信息,读取用户选择
            if(ret == 0) 
            {
                //暂停后选择继续游戏,则重新绘制蛇头和蛇身继续执行程序
                Node* tmp = head->next;

                //绘制蛇身
                wattron(scrgame, COLOR_PAIR(attribute.color.phycol) );
                while(tmp != sna->physics->head)
                {
                mvwaddwstr(scrgame, tmp->val.y, tmp->val.x, phychar[attribute.color.snaphy]);
                tmp = tmp->next;
                wrefresh(scrgame);
                }
                wattroff(scrgame, COLOR_PAIR(attribute.color.phycol));
                
                //绘制蛇头
                wattron(scrgame, COLOR_PAIR(attribute.color.headcol) );
                mvwaddwstr(scrgame, head->val.y, head->val.x == -1 ? 0 : val.x, phychar[attribute.color.snahead]);
                wattroff(scrgame, COLOR_PAIR(attribute.color.headcol));
                
                //绘制豆子
                mvwaddwstr(scrgame, nap.y, nap.x, NAP);

                //将绘制的数据进行刷新到窗口
                wrefresh(scrgame);
                usleep(attribute.speed);

                continue;//进行下一次用户按键读取 
                }
            else 
            {
              input = 3; //暂停后选择退出,break终止循环退出游戏
              break;
            }
        }
        //自动走完全图,在头生成豆子的测试
        // if((val.x == 1 && sna->dire == SLEFT) || (val.x == attribute.mapwidth -3 && sna->dire == SRIGHT)) 
        // input = KEY_DOWN;
        // else if(val.x == 1 && sna->dire == SDOWN) 
        // input = KEY_RIGHT;
        // else if(val.x == attribute.mapwidth - 3 && sna->dire == SDOWN) 
        // input = KEY_LEFT;


        //根据用户输入来进行蛇头的移动
        if((input == KEY_UP && sna->dire != SDOWN) || ((input == ERR || input == KEY_DOWN) && sna->dire == SUP))
        {
            val.y--;
            sna->dire = SUP;
        }   
        else if((input == KEY_DOWN && sna->dire != SUP)|| ((input == ERR || input == KEY_UP) && sna->dire == SDOWN))
        {
            val.y++;
            sna->dire = SDOWN;
        }
        else if((input == KEY_LEFT && sna->dire != SRIGHT)|| ((input == ERR || input == KEY_RIGHT) && sna->dire == SLEFT))
        {
            val.x-=2;
            sna->dire = SLEFT;
        }
        else if((input == KEY_RIGHT && sna->dire != SLEFT) || ((input == ERR || input == KEY_LEFT) && sna->dire == SRIGHT))
        {
            val.x+=2;
            sna->dire = SRIGHT;
        }

        PushFront(sna->physics, val); //将蛇头位置记录进链表中

        //蛇头需要移动,移动时,在蛇头位置绘制一个蛇身符号,在新的蛇头位置绘制新的蛇头,在视觉上就是蛇在移动
        //将上一次蛇头位置绘制为蛇身
        wattron(scrgame, COLOR_PAIR(attribute.color.phycol));
        mvwaddwstr(scrgame, sna->physics->head->next->next->val.y,  sna->physics->head->next->next->val.x, phychar[attribute.color.snaphy]);
        wattroff(scrgame, COLOR_PAIR(attribute.color.phycol));
       
        //判断是否撞墙,由于撞墙后还需绘制出撞墙的蛇头,即不能立即结束,需要在画一次画面,因此没有用break直接跳出循环,而是执行后续程序
        if(val.x == -1 || val.x == attribute.mapwidth - 1  || val.y == 0 || val.y == attribute.maplength - 1)
        {
            falg = FALSE;
            input = 2;
        }

        //判断是否吃到自己身体
        if(val.x != -1 && bitmap[val.y][val.x / 2 + 1] == 1) 
        {
            falg = FALSE;
            input = 1;
        }

        if(falg) //判断游戏是否失败,未失败则将移动后蛇头的位置加入位图中
        bitmap[val.y][val.x / 2 + 1] = 1;

        //在头位置生成豆子的测试,为了检验游戏胜利的显示情况
        // nap.x = val.x;
        // nap.y = val.y;

        //判断是否吃到豆子
        if(val.x == nap.x && val.y == nap.y)
        {
            sna->score++;   //吃到豆子成绩加1
            if(sna->score == maxscore)   //判断是否胜利,达到该地图最大面积即胜利,
            {
                falg = FALSE;
                input = 0;
            }
            //还未胜利,吃掉豆子后重新生成豆子
            while(bitmap[nap.y][nap.x / 2 + 1] == 1)
            {
                nap.x = rand() % (attribute.mapwidth - 3) ;
                if(nap.x % 2 == 0) nap.x++;
                nap.y = rand() % (attribute.maplength - 2) + 1;
            } 
        }
        //没吃到豆子,则蛇需要移动,将蛇尾删除,蛇头打印在新的位置,即可做到蛇的移动
        else 
        {
            mvwprintw(scrgame, sna->physics->head->prev->val.y,  sna->physics->head->prev->val.x, "  "); //打印空格,视觉上移动蛇尾
            bitmap[sna->physics->head->prev->val.y][sna->physics->head->prev->val.x / 2 + 1] = 0; //从位图里移除
            PopBack(sna->physics); //从数据结构里移除
        }


    head = sna->physics->head->next; //更新蛇头位置
    //打印最新蛇头
    wattron(scrgame, COLOR_PAIR(attribute.color.headcol) );
    mvwaddwstr(scrgame, val.y, val.x == -1 ? 0 : val.x, phychar[attribute.color.snahead]);
    wattroff(scrgame, COLOR_PAIR(attribute.color.headcol));

    //将数据刷新到窗口内
    wrefresh(scrgame);

    //打印成绩并刷新
    mvwprintw(scrscore, attribute.maplength / 2 - 5, 9, "%d", sna->score);
    wrefresh(scrscore);
    
    //短暂休眠后进行下一次移动
    usleep(attribute.speed);
    }
 
    //游戏结束,刷新缓冲区,清除屏幕等操作
    flushinp();
    nodelay(scrgame, FALSE);
    wclear(scrscore);

    wrefresh(scrscore);

    //游戏胜利或者失败后退出,input记录退出信息,input为3时是暂停后退出,不计入成绩
    if(input != 3)
    {
        //打印游戏相关成绩
        wclear(scrgame);
        mvwprintw(scrgame, attribute.maplength / 2,  attribute.mapwidth / 2 - 4, "你%s!", massage4[input]);
        mvwprintw(scrgame, attribute.maplength / 5,  attribute.mapwidth / 2 - 3, "得分：%d",sna->score);
        mvwprintw(scrgame, attribute.maplength / 2 + 2,  attribute.mapwidth / 2 - 6, "按任意键继续");

        //将本次游戏成绩保存
        onesc re = {{0}, sna->score, massage2[GET_MAPPOS(attribute.maplength)], massage3[GET_SPEEDPOS(attribute.speed)], massage4[input]};
        
        //时间转化字符串相关程序
        time_t timestamp = time(NULL); // 获取时间戳
        struct tm *timeinfo;
        char timebuffer[BUFFERSIZE];
        // 将时间戳转换为本地时间结构
        timeinfo = localtime(&timestamp);
        // 格式化在timebuffer中输出为 "YYYY-MM-DD HH:MM:SS"
        strftime(timebuffer, sizeof(timebuffer), "%Y-%m-%d %H:%M:%S", timeinfo);

        //将所有数据放入缓冲区buffer后,加入到全局的成绩数据结构里
        char* buffer = (char*)malloc(BUFFERSIZE);
        snprintf(buffer, BUFFERSIZE, "%s       %-6d     %s         %s        %s",
            timebuffer, re.score, re.map, re.speed, re.resu);  
        Push(&allgameresult, buffer); 

        //清除窗口,回收资源
        wrefresh(scrgame);
        wgetch(scrgame);
    }


}

//释放相关数据结构
void DestroySnake(Snake* sna)
{
     Destroy(sna->physics);
     free(sna);
}
